namespace NServiceBus.AcceptanceTests.Sagas;

using System;
using System.Threading;
using System.Threading.Tasks;
using AcceptanceTesting;
using EndpointTemplates;
using NUnit.Framework;

public class When_replying_to_originator_from_a_timeout : NServiceBusAcceptanceTest
{
    [Test, CancelAfter(15_000)]
    public async Task Should_route_the_message_to_the_endpoint_starting_the_saga(CancellationToken cancellationToken = default)
    {
        Requires.DelayedDelivery();

        var context = await Scenario.Define<Context>()
            .WithEndpoint<Endpoint>(b => b.When(session => session.SendLocal(new InitiateRequestingSaga())))
            .Done(c => c.DidRequestingSagaGetTheResponse)
            .Run(cancellationToken);

        Assert.That(context.DidRequestingSagaGetTheResponse, Is.True);
    }

    public class Context : ScenarioContext
    {
        public bool DidRequestingSagaGetTheResponse { get; set; }
    }

    public class Endpoint : EndpointConfigurationBuilder
    {
        public Endpoint() => EndpointSetup<DefaultServer>();

        public class RequestResponseRequestingSaga3(Context testContext)
            : Saga<RequestResponseRequestingSaga3.RequestResponseRequestingSagaData3>,
                IAmStartedByMessages<InitiateRequestingSaga>,
                IHandleMessages<ResponseFromOtherSaga>
        {
            public Task Handle(InitiateRequestingSaga message, IMessageHandlerContext context) =>
                context.SendLocal(new RequestToRespondingSaga
                {
                    SomeIdThatTheResponseSagaCanCorrelateBackToUs = Data.CorrIdForResponse
                });

            public Task Handle(ResponseFromOtherSaga message, IMessageHandlerContext context)
            {
                testContext.DidRequestingSagaGetTheResponse = true;

                MarkAsComplete();

                return Task.CompletedTask;
            }

            protected override void ConfigureHowToFindSaga(SagaPropertyMapper<RequestResponseRequestingSagaData3> mapper) =>
                mapper.MapSaga(s => s.CorrIdForResponse)
                    .ToMessage<InitiateRequestingSaga>(m => m.Id)
                    .ToMessage<ResponseFromOtherSaga>(m => m.SomeCorrelationId);

            public class RequestResponseRequestingSagaData3 : ContainSagaData
            {
                public virtual Guid CorrIdForResponse { get; set; }
            }
        }

        public class RequestResponseRespondingSaga3 : Saga<RequestResponseRespondingSaga3.RequestResponseRespondingSagaData3>,
            IAmStartedByMessages<RequestToRespondingSaga>,
            IHandleTimeouts<RequestResponseRespondingSaga3.DelayReply>
        {
            public Task Handle(RequestToRespondingSaga message, IMessageHandlerContext context) => RequestTimeout<DelayReply>(context, TimeSpan.FromMilliseconds(1));

            public Task Timeout(DelayReply state, IMessageHandlerContext context) =>
                //reply to originator must be used here since the sender of the incoming message is the TimeoutManager and not the requesting saga
                ReplyToOriginator(context, new ResponseFromOtherSaga
                {
                    SomeCorrelationId = Data.CorrIdForRequest
                });

            protected override void ConfigureHowToFindSaga(SagaPropertyMapper<RequestResponseRespondingSagaData3> mapper) =>
                mapper.MapSaga(s => s.CorrIdForRequest)
                    .ToMessage<RequestToRespondingSaga>(m => m.SomeIdThatTheResponseSagaCanCorrelateBackToUs);


            public class RequestResponseRespondingSagaData3 : ContainSagaData
            {
                public virtual Guid CorrIdForRequest { get; set; }
            }

            public class DelayReply;
        }
    }

    public class InitiateRequestingSaga : ICommand
    {
        public Guid Id { get; set; } = Guid.NewGuid();
    }

    public class RequestToRespondingSaga : ICommand
    {
        public Guid SomeIdThatTheResponseSagaCanCorrelateBackToUs { get; set; }
    }

    public class ResponseFromOtherSaga : IMessage
    {
        public Guid SomeCorrelationId { get; set; }
    }
}